use crate::rtmp::chunk::packetizer::ChunkPacketizer;
use crate::rtmp::chunk::ChunkInfo;
use crate::rtmp::net_connection::errors::NetConnectionError;
use crate::rtmp::{chunk, messages};
use bytesio::bytesio::TNetIO;
use indexmap::IndexMap;
use std::sync::Arc;
use tokio::sync::Mutex;
use xflv::amf0::amf0_writer::Amf0Writer;
use xflv::amf0::Amf0ValueType;

// 连接属性结构体
#[derive(Debug, Default, Clone)]
pub struct ConnectProperties {
    // 服务器应用名称
    pub app: Option<String>,
    // Flash Player 版本
    pub flash_ver: Option<String>,
    // 源 SWF 文件的URL
    pub swf_url: Option<String>,
    // 服务器 URL
    pub tc_url: Option<String>,
    // 是否使用代理
    pub fpad: Option<bool>,
    // 能力值，默认为：15
    pub capabilities: Option<f64>,
    // 音频解码器，默认为：4071
    pub audio_codecs: Option<f64>,
    // 视频解码器，默认为：252
    pub video_codecs: Option<f64>,
    // 视频功能，默认为：1
    pub video_function: Option<f64>,
    // 对象编码
    pub object_encoding: Option<f64>,
    // 页面 URL
    pub page_url: Option<String>,
    // 发布类型
    pub pub_type: Option<String>,
}

impl ConnectProperties {
    pub fn new(app_name: String) -> Self {
        Self {
            app: Some(app_name),
            flash_ver: Some("LNX 9,0,124,2".to_string()),
            swf_url: Some("".to_string()),
            tc_url: Some("".to_string()),
            fpad: Some(false),
            capabilities: Some(15_f64),
            audio_codecs: Some(4071_f64),
            video_codecs: Some(252_f64),
            video_function: Some(1_f64),
            object_encoding: Some(0_f64),
            page_url: Some("".to_string()),
            pub_type: Some("nonprivate".to_string()),
        }
    }

    // 创建一个空的连接属性
    pub fn new_none() -> Self {
        Self {
            app: None,
            flash_ver: None,
            swf_url: None,
            tc_url: None,
            fpad: None,
            capabilities: None,
            audio_codecs: None,
            video_codecs: None,
            video_function: None,
            object_encoding: None,
            page_url: None,
            pub_type: None,
        }
    }
}

// 网络连接结构体
pub struct NetConnection {
    // AMF0 写入器
    amf0_writer: Amf0Writer,
    // 块打包器
    packetizer: ChunkPacketizer,
}

impl NetConnection {
    pub fn new(io: Arc<Mutex<Box<dyn TNetIO + Send + Sync>>>) -> Self {
        Self {
            amf0_writer: Amf0Writer::new(),
            packetizer: ChunkPacketizer::new(io),
        }
    }

    // 写入块
    async fn write_chunk(&mut self) -> Result<(), NetConnectionError> {
        let data = self.amf0_writer.extract_current_bytes();
        let mut chunk_info = ChunkInfo::new(
            // 块流 ID
            chunk::define::csid_type::COMMAND_AMF0_AMF3,
            // 块类型
            chunk::define::chunk_type::TYPE_0,
            // 时间戳
            0,
            // 消息长度
            data.len() as u32,
            // 消息类型 ID
            messages::define::msg_type_id::COMMAND_AMF0,
            // 消息流 ID
            0,
            // 数据
            data,
        );

        self.packetizer.write_chunk(&mut chunk_info).await?;
        Ok(())
    }

    // 使用 AMF0 写入连接请求
    pub async fn write_connect(
        &mut self,
        transaction_id: &f64,
        properties: &ConnectProperties,
    ) -> Result<(), NetConnectionError> {
        // 写入命令名称
        self.amf0_writer.write_string(&String::from("connect"))?;
        // 写入事务ID
        self.amf0_writer.write_number(transaction_id)?;
        // 创建属性映射
        let mut properties_map = IndexMap::new();

        // 根据属性设置AMF0值
        if let Some(app) = properties.app.clone() {
            properties_map.insert(String::from("app"), Amf0ValueType::UTF8String(app));
        }

        if let Some(pub_type) = properties.pub_type.clone() {
            properties_map.insert(String::from("type"), Amf0ValueType::UTF8String(pub_type));
        }

        if let Some(flash_ver) = properties.flash_ver.clone() {
            properties_map.insert(
                String::from("flashVer"),
                Amf0ValueType::UTF8String(flash_ver),
            );
        }

        if let Some(tc_url) = properties.tc_url.clone() {
            properties_map.insert(String::from("tcUrl"), Amf0ValueType::UTF8String(tc_url));
        }

        if let Some(swf_url) = properties.swf_url.clone() {
            properties_map.insert(String::from("swfUrl"), Amf0ValueType::UTF8String(swf_url));
        }

        if let Some(page_url) = properties.page_url.clone() {
            properties_map.insert(String::from("pageUrl"), Amf0ValueType::UTF8String(page_url));
        }

        if let Some(fpad) = properties.fpad {
            properties_map.insert(String::from("fpad"), Amf0ValueType::Boolean(fpad));
        }

        if let Some(capabilities) = properties.capabilities {
            properties_map.insert(
                String::from("capabilities"),
                Amf0ValueType::Number(capabilities),
            );
        }

        if let Some(audio_codecs) = properties.audio_codecs {
            properties_map.insert(
                String::from("audioCodecs"),
                Amf0ValueType::Number(audio_codecs),
            );
        }

        if let Some(video_codecs) = properties.video_codecs {
            properties_map.insert(
                String::from("videoCodecs"),
                Amf0ValueType::Number(video_codecs),
            );
        }

        if let Some(video_function) = properties.video_function {
            properties_map.insert(
                String::from("videoFunction"),
                Amf0ValueType::Number(video_function),
            );
        }

        if let Some(object_encoding) = properties.object_encoding {
            properties_map.insert(
                String::from("objectEncoding"),
                Amf0ValueType::Number(object_encoding),
            );
        }
        // 写入属性对象
        self.amf0_writer.write_object(&properties_map)?;

        // 写入块
        self.write_chunk().await
    }

    // 写入连接响应
    pub async fn write_connect_response(
        &mut self,
        transaction_id: &f64,
        fmsver: &str,
        capabilities: &f64,
        code: &str,
        level: &str,
        description: &str,
        encoding: &f64,
    ) -> Result<(), NetConnectionError> {
        self.amf0_writer.write_string(&String::from("_result"))?; // 写入命令名称
                                                                  // 写入事务ID
        self.amf0_writer.write_number(transaction_id)?;
        // 创建属性映射A
        let mut properties_map_a = IndexMap::new();

        properties_map_a.insert(
            String::from("fmsVer"),
            Amf0ValueType::UTF8String(fmsver.to_owned()),
        );
        properties_map_a.insert(
            String::from("capabilities"),
            Amf0ValueType::Number(*capabilities),
        );
        // 写入属性对象A
        self.amf0_writer.write_object(&properties_map_a)?;
        // 创建属性映射B
        let mut properties_map_b = IndexMap::new();

        properties_map_b.insert(
            String::from("level"),
            Amf0ValueType::UTF8String(level.to_owned()),
        );
        properties_map_b.insert(
            String::from("code"),
            Amf0ValueType::UTF8String(code.to_owned()),
        );
        properties_map_b.insert(
            String::from("description"),
            Amf0ValueType::UTF8String(description.to_owned()),
        );
        properties_map_b.insert(
            String::from("objectEncoding"),
            Amf0ValueType::Number(*encoding),
        );
        // 写入属性对象B
        self.amf0_writer.write_object(&properties_map_b)?;
        // 写入块
        self.write_chunk().await
    }

    pub async fn write_create_stream(
        &mut self,
        transaction_id: &f64,
    ) -> Result<(), NetConnectionError> {
        // 写入命令名称
        self.amf0_writer
            .write_string(&String::from("createStream"))?;
        // 写入事务 ID
        self.amf0_writer.write_number(transaction_id)?;
        // 写入空值
        self.amf0_writer.write_null()?;

        self.write_chunk().await
    }

    // 写入创建响应流
    pub async fn write_create_stream_response(
        &mut self,
        transaction_id: &f64,
        stream_id: &f64,
    ) -> Result<(), NetConnectionError> {
        self.amf0_writer.write_string(&String::from("_result"))?;
        self.amf0_writer.write_number(transaction_id)?;
        self.amf0_writer.write_null()?;
        self.amf0_writer.write_number(stream_id)?;

        self.write_chunk().await
    }

    // 写入获取流长度请求
    pub async fn write_get_stream_length(
        &mut self,
        transaction_id: &f64,
        stream_name: &String,
    ) -> Result<(), NetConnectionError> {
        self.amf0_writer
            .write_string(&String::from("getStreamLength"))?;
        self.amf0_writer.write_number(transaction_id)?;
        self.amf0_writer.write_null()?;
        self.amf0_writer.write_string(stream_name)?;

        self.write_chunk().await
    }

    // 写入错误响应
    pub async fn error(
        &mut self,
        transaction_id: &f64,
        code: &str,
        level: &str,
        description: &str,
    ) -> Result<(), NetConnectionError> {
        self.amf0_writer.write_string(&String::from("_error"))?;
        self.amf0_writer.write_number(transaction_id)?;
        self.amf0_writer.write_null()?;
        // 创建属性映射
        let mut properties_map = IndexMap::new();

        properties_map.insert(
            String::from("level"),
            Amf0ValueType::UTF8String(level.to_owned()),
        );
        properties_map.insert(
            String::from("code"),
            Amf0ValueType::UTF8String(code.to_owned()),
        );
        properties_map.insert(
            String::from("description"),
            Amf0ValueType::UTF8String(description.to_owned()),
        );
        // 写入属性对象
        self.amf0_writer.write_object(&properties_map)?;

        self.write_chunk().await
    }
}
